﻿/*
 * Created by SharpDevelop.
 * User: homeplant
 * Date: 2011.07.26.
 * Time: 15:17
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using RTMP;
using DirectShowLib;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Collections.Generic;

using System.Linq;
using System.Text;

namespace RTMP
{
    public partial class Video : UserControl
    {
        private StreamHandler m_VideoHandler = new StreamHandler();
        private StreamHandler m_AudioHandler = new StreamHandler();
        private delegate void SetupGraphHandler(Tag tvideo,Tag taudio);
        private delegate void DeliveryDelegate(int tag, ulong StartTime, ulong EndTime);
        private delegate void setVideoWindowHandler();
        private static ManualResetEvent WaitForPacket = new ManualResetEvent(false);
        private AMMediaType m_VideoMediatype = null, m_AudioMediaType = null;
        private IFilterGraph2 m_FilterGraph = null;
        private IMediaControl m_mediaCtrl = null;
        private IVideoWindow videoWindow = null;
        private INetStreamFilterConfig m_VideoConfig = null, m_AudioConfig = null;
        public Queue<Tag> T = new Queue<Tag>();
        public event EventHandler Completed = null;
        private NetStream m_ns = null;
        private Control self;
        private Thread deliveryThread = null;
        private static readonly object comlock = new object();
        private static readonly object deliverlock = new object();
        private IBaseFilter fltSource = null;
        private Thread EventLoop = null;
        private bool bWaitingPacket = false;
        private bool m_bAudioDisable = false;
        private bool m_bVideoDisable = false;
        private Tag tvideo, taudio;
        private bool bVideoFound = false;
        private bool bVideoSet = false;
        private bool bAudioSet = false;
        private bool bAudioFound = false;
        private bool bSetupRunning = true;
        private bool bPlaying = false;
        private bool bVideoError = false;
        private bool bAudioError = false;
        private UInt64 VideoStartTime = 0, VideoEndTime = 0, VideoTimediff = 0;
        private UInt64 AudioStartTime = 0, AudioEndTime = 0, AudioTimediff = 0;

#if DEBUG
        DsROTEntry m_DsRot;
#endif

        public Video()
        {
            InitializeComponent();
            self = this;
        }
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            DestroyVideo();

        }
        public void attachNetStream(NetStream ns)
        {
            m_ns = ns;
            m_ns.updater = EnqueueTag;
            EventLoop = new Thread(new ThreadStart(EventWait));
            deliveryThread = new Thread(new ThreadStart(ThreadProc));
        }
        public void detachNetStream()
        {
            DestroyVideo();
        }
        private void DestroyVideo()
        {
            if (m_ns != null)
            {
                m_ns.updater = null;
                m_ns = null;
                Thread.Sleep(500);
            }
            if (deliveryThread != null && deliveryThread.IsAlive)
            {
                Thread.Sleep(500);
                if (deliveryThread.IsAlive)
                    deliveryThread.Abort();
            }
            T.Clear();
            if (m_mediaCtrl != null)
            {
                m_mediaCtrl.Stop();
                try
                {
                    Marshal.ReleaseComObject(m_mediaCtrl);
                    m_mediaCtrl = null;
                }
                catch { }
            }
            if (videoWindow != null)
            {
                try
                {
                    Marshal.ReleaseComObject(videoWindow);
                    videoWindow = null;
                }
                catch { }
            }
            if (m_VideoConfig != null)
            {
                try
                {
                    Marshal.ReleaseComObject(m_VideoConfig);
                    m_VideoConfig = null;
                }
                catch { }
            }
            if (m_AudioConfig != null)
            {
                try
                {
                    Marshal.ReleaseComObject(m_AudioConfig);
                    m_AudioConfig = null;
                }
                catch { }
            }
            if (fltSource != null)
            {
                try
                {
                    Marshal.ReleaseComObject(fltSource);
                }
                catch { }
            }
            if (m_FilterGraph != null)
            {
                try
                {
                    Marshal.ReleaseComObject(m_FilterGraph);
                    m_FilterGraph = null;
                }
                catch { }
            }
            m_AudioMediaType = null;
            m_VideoMediatype = null;
            m_VideoHandler = null;
            m_AudioHandler = null;
        }
        private void ThreadProc()
        {
            bool bVideoOk = false;
            try
            {
                while (m_ns != null)
                {
                    int hr = 0;
                    if (T.Count > 0)
                    {
                        Tag t;
                        t = T.Dequeue();
                        if (m_mediaCtrl == null)
                        {
                            T.Clear();
                            continue;
                        }
                        if (t.TagType == 9)
                        {
                            if (!bVideoOk)
                                bVideoOk = true;
                            m_VideoHandler.CurrTag = t;
                            hr = m_VideoConfig.DeliverPacket((uint)t.StartTime, (uint)t.EndTime);
                           // Debug.WriteLine("Video sample ok hr{0}", hr);
                        }
                        else
                        {
                            if (!bVideoOk)
                                continue;
                            m_AudioHandler.CurrTag = t;
                            hr = m_AudioConfig.DeliverPacket((uint)t.StartTime, (uint)t.EndTime);
                        }
                    }
                    else
                    {
                        Debug.WriteLine("wait 1 s");
                        Thread.Sleep(1000);
                    }
                }
                Debug.WriteLine("Push Thread exits");
            }
            catch (ThreadAbortException ab)
            {
                Debug.WriteLine("Pusher thread aborted(" + ab.Message + ")");
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Pusher thread exception: "+ex.Message);
            }
            finally
            {
            }
        }
        private void EnqueueTag(Tag t)
        {
            if (bSetupRunning)
            {
                if (t.TagType == 9)
                    VideoTimediff = t.EndTime;
                if (t.TagType == 8)
                    AudioTimediff = t.EndTime;
                if (!bVideoFound && t.TagType == 9 && !m_bVideoDisable)
                {
                    bVideoFound = true;
                    tvideo = t;
                    if ((bVideoFound && bAudioFound) || (bVideoFound && m_bAudioDisable))
                        Invoke(new SetupGraphHandler(setupGraph),new object[]{tvideo, taudio});
                    return;
                }
                if (!bAudioFound && t.TagType == 8 &&  !m_bAudioDisable)
                {
                    bAudioFound = true;
                    taudio = t;
                    if ((bVideoFound && bAudioFound) || (bAudioFound && m_bVideoDisable))
                        Invoke(new SetupGraphHandler(setupGraph), new object[] { tvideo, taudio });
                }
                if ((bVideoSet && bAudioSet) || (bVideoSet && m_bAudioDisable) || (bAudioSet && m_bVideoDisable))
                {
                    Invoke(new setVideoWindowHandler(setVideoWindow));
                    if (m_mediaCtrl != null)
                        m_mediaCtrl.Run();
                    bPlaying = true;
                    bSetupRunning = false;
                    EventLoop = new Thread(new ThreadStart(EventWait));
                    EventLoop.Start();
                    deliveryThread = new Thread(new ThreadStart(ThreadProc));
                    deliveryThread.Start();
                    return;
                }
            }
            else if (bPlaying)
            {
                if (t.TagType == 9 && (!bVideoSet || m_bVideoDisable))
                    return;
                if (t.TagType == 8 && (!bAudioSet || m_bAudioDisable))
                    return;
                if (t.TagType == 9)
                {
                    VideoStartTime = t.StartTime - VideoTimediff;
                    VideoEndTime = t.EndTime - VideoTimediff;
                    t.StartTime = VideoStartTime;
                    t.EndTime = VideoEndTime;
                }
                else
                {
                    AudioStartTime = t.StartTime - AudioTimediff;
                    AudioEndTime = t.EndTime - AudioTimediff;
                    t.StartTime = AudioStartTime;
                    t.EndTime = AudioEndTime;
                }
                try
                {
                    if (m_mediaCtrl == null)
                        return;
                    if (t.TagType == 9 && (m_VideoHandler == null || m_VideoMediatype == null || m_VideoConfig == null))
                        return;
                    if (t.TagType == 8 && (m_AudioHandler == null || m_AudioMediaType == null || m_AudioConfig == null))
                        return;
                    T.Enqueue(t);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception in enqueuetag: " + ex.Message);
                }
            }
        }
        private void setupGraph(Tag tvideo, Tag taudio)
        {
            int hr;
            m_FilterGraph = new FilterGraph() as IFilterGraph2;
            ICaptureGraphBuilder2 icgb2 = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            hr = icgb2.SetFiltergraph(m_FilterGraph);
            DsError.ThrowExceptionForHR(hr);
            m_VideoHandler.CurrTag = tvideo;
            m_AudioHandler.CurrTag = taudio;
#if DEBUG
            m_DsRot = new DsROTEntry(m_FilterGraph);
#endif
            IBaseFilter fltVDecoder = null;
            IBaseFilter fltVRenderer = null;
            IBaseFilter fltADecoder = null;
            IBaseFilter fltARenderer = null;

            IPin pinVSourceOut = null;
            IPin pinVDecoderIn = null;
            IPin pinVDecoderOut = null;
            IPin pinVRendererIn = null;
            IPin pinASourceOut = null;
            IPin pinADecoderIn = null;
            IPin pinADecoderOut = null;
            IPin pinARendererIn = null;
            try
            {
                if (!m_bVideoDisable)
                {
                    fltSource = (IBaseFilter)new NetStreamFilter();
                    hr = m_FilterGraph.AddFilter(fltSource, "NetStreamFilter");
                    if (m_mediaCtrl != null)
                        m_mediaCtrl.Stop();
                    if (!createMediaType(tvideo, out m_VideoMediatype))
                        throw new Exception("Can't create video media type");
                    pinVSourceOut = DsFindPin.ByDirection(fltSource, PinDirection.Output, 0);
                    m_VideoConfig = (INetStreamFilterConfig)pinVSourceOut;
                    m_VideoConfig.SetPinMediaType(m_VideoMediatype);
                    m_VideoConfig.SetCallback(m_VideoHandler);
                    bool res = findFilter("ffdshow video decoder", out fltVDecoder);
                    if (!res)
                        throw new Exception("Can't load ffdshow video decoder");
                    hr = m_FilterGraph.AddFilter(fltVDecoder, "video decoder");
                    pinVDecoderIn = DsFindPin.ByDirection(fltVDecoder, PinDirection.Input, 0);
                    hr = m_FilterGraph.Connect(pinVSourceOut, pinVDecoderIn);
                    Marshal.ThrowExceptionForHR(hr);
                    pinVDecoderOut = DsFindPin.ByDirection(fltVDecoder, PinDirection.Output, 0);
                    res = findFilter("video renderer", out fltVRenderer);
                    if (!res)
                        throw new Exception("Can't load video renderer");
                    m_FilterGraph.AddFilter(fltVRenderer, "video renderer");
                    pinVRendererIn = DsFindPin.ByDirection(fltVRenderer, PinDirection.Input, 0);
                    hr = m_FilterGraph.Connect(pinVDecoderOut, pinVRendererIn);
                    if (hr != 0)
                        Marshal.ThrowExceptionForHR(hr);
                    bVideoSet = true;
                }
            }
            catch
            {
                bVideoError=true;
                m_bVideoDisable = true;
                m_VideoConfig = null; m_VideoHandler = null; m_VideoMediatype = null;
            }
            try
            {
                if (!m_bAudioDisable)
                {
                    if (!createMediaType(taudio, out m_AudioMediaType))
                        throw new Exception("Can't create audio media type");
                    pinASourceOut = DsFindPin.ByDirection(fltSource, PinDirection.Output, 1);
                    m_AudioConfig = (INetStreamFilterConfig)pinASourceOut;
                    m_AudioConfig.SetPinMediaType(m_AudioMediaType);
                    m_AudioConfig.SetCallback(m_AudioHandler);
                    bool res = findFilter("ffdshow audio decoder", out fltADecoder);
                    if (!res)
                        throw new Exception("Can't load ffdshow audio decoder");
                    hr = m_FilterGraph.AddFilter(fltADecoder, "audio decoder");
                    pinADecoderIn = DsFindPin.ByDirection(fltADecoder, PinDirection.Input, 0);
                    hr = m_FilterGraph.Connect(pinASourceOut, pinADecoderIn);
                    if (hr != 0)
                        Marshal.ThrowExceptionForHR(hr);
                    pinADecoderOut = DsFindPin.ByDirection(fltADecoder, PinDirection.Output, 0);
                    res = findFilter("default directsound device", FilterCategory.AudioRendererCategory, out fltARenderer);
                    if (!res)
                        throw new Exception("Can't load audio renderer");
                    m_FilterGraph.AddFilter(fltARenderer, "audio renderer");
                    pinARendererIn = DsFindPin.ByDirection(fltARenderer, PinDirection.Input, 0);
                    hr = m_FilterGraph.Connect(pinADecoderOut, pinARendererIn);
                    if (hr != 0)
                        Marshal.ThrowExceptionForHR(hr);
                    bAudioSet = true;
                }
            }
            catch
            {
                bAudioError = true;
                m_bAudioDisable = true;
                m_AudioMediaType = null; m_AudioHandler = null; m_AudioConfig = null;
            }
            try { if (fltVDecoder != null) Marshal.ReleaseComObject(fltVDecoder); }
            catch { }
            try { if (fltVRenderer != null) Marshal.ReleaseComObject(fltVRenderer); }
            catch { }
            try { if (pinVDecoderIn != null) Marshal.ReleaseComObject(pinVDecoderIn); }
            catch { }
            try { if (pinVDecoderOut != null) Marshal.ReleaseComObject(pinVDecoderOut); }
            catch { }
            try { if (pinVRendererIn != null) Marshal.ReleaseComObject(pinVRendererIn); }
            catch { }
            try { if (fltADecoder != null) Marshal.ReleaseComObject(fltADecoder); }
            catch { }
            try { if (fltARenderer != null) Marshal.ReleaseComObject(fltARenderer); }
            catch { }
            try { if (pinADecoderIn != null) Marshal.ReleaseComObject(pinADecoderIn); }
            catch { }
            try { if (pinADecoderOut != null) Marshal.ReleaseComObject(pinADecoderOut); }
            catch { }
            try { if (pinARendererIn != null) Marshal.ReleaseComObject(pinARendererIn); }
            catch { }
            try { if (icgb2 != null) Marshal.ReleaseComObject(icgb2); }
            catch { }
        }
        private void setVideoWindow()
        {
            if (!bVideoSet)
                return;
            int hr = 0;
            videoWindow = m_FilterGraph as IVideoWindow;
            hr = videoWindow.put_Owner(((Control)this).Handle);
            if (hr >= 0)
            {
                hr = videoWindow.put_WindowStyle((WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings));
                DsError.ThrowExceptionForHR(hr);
                hr = videoWindow.put_Visible(OABool.True);
                DsError.ThrowExceptionForHR(hr);
                Rectangle rc = ((Control)this).ClientRectangle;
                hr = videoWindow.SetWindowPosition(0, 0, Width, Height);
                DsError.ThrowExceptionForHR(hr);
            }
            m_mediaCtrl = m_FilterGraph as IMediaControl;
        }
        private void EventWait()
        {
            //const int E_ABORT = unchecked((int)0x80004004);
            int hr;
            IntPtr p1, p2;
            EventCode ec;
            EventCode exitCode = 0;

            IMediaEvent pEvent = (IMediaEvent)m_FilterGraph;
            if (pEvent == null)
                return;
            do
            {
                for (
                    hr = pEvent.GetEvent(out ec, out p1, out p2, 100);
                    hr >= 0;
                    hr = pEvent.GetEvent(out ec, out p1, out p2, 100)
                    )
                {
                    Debug.WriteLine(ec);
                    switch (ec)
                    {
                        case EventCode.Paused:
                            break;
                        case EventCode.Complete:
                        case EventCode.ErrorAbort:
                        case EventCode.UserAbort:
                            exitCode = ec;
                            hr = pEvent.FreeEventParams(ec, p1, p2);
                            DsError.ThrowExceptionForHR(hr);
                            break;
                        default:
                            hr = pEvent.FreeEventParams(ec, p1, p2);
                            break;
                    }
                }
            } while (exitCode == 0 && m_ns != null);
            if (Completed != null)
            {
                StreamCompletedArgs ca = new StreamCompletedArgs(exitCode);
                Completed(this, ca);
            }

        }
        private bool findFilter(string name, IFilterGraph2 graph, out IBaseFilter fltr)
        {
            fltr = null;
            IEnumFilters ef;
            m_FilterGraph.EnumFilters(out ef);
            IBaseFilter[] ibf = new IBaseFilter[1];
            while (ef.Next(1, ibf, IntPtr.Zero) == 0)
            {
                FilterInfo fi;
                ibf[0].QueryFilterInfo(out fi);
                if (fi.achName.ToLower() == name)
                {
                    fltr = ibf[0];
                    return true;
                }
            }

            return false;
        }
        private bool findFilter(string name, Guid filterCategory, out IBaseFilter fltr)
        {
            fltr = null;
            DsDevice[] decoders = DsDevice.GetDevicesOfCat(filterCategory);
            foreach (DsDevice decoder in decoders)
            {
                if (decoder.Name.ToLower().Contains(name))
                {
                    object obj = null;
                    Guid bfGuid = typeof(IBaseFilter).GUID;
                    decoder.Mon.BindToObject(null, null, ref bfGuid, out obj);
                    fltr = obj as IBaseFilter;
                    return true;
                }
            }
            return false;
        }
        private bool findFilter(string name, out IBaseFilter fltr)
        {
            fltr = null;
            DsDevice[] decoders = DsDevice.GetDevicesOfCat(FilterCategory.LegacyAmFilterCategory);
            foreach (DsDevice decoder in decoders)
            {
                if (decoder.Name.ToLower().Contains(name))
                {
                    object obj = null;
                    Guid bfGuid = typeof(IBaseFilter).GUID;
                    decoder.Mon.BindToObject(null, null, ref bfGuid, out obj);
                    fltr = obj as IBaseFilter;
                    return true;
                }
            }
            return false;
        }
        private void enumFilters()
        {
            IEnumFilters ef;
            m_FilterGraph.EnumFilters(out ef);
            IBaseFilter[] ibf = new IBaseFilter[1];
            while (ef.Next(1, ibf, IntPtr.Zero) == 0)
            {
                FilterInfo fi;
                ibf[0].QueryFilterInfo(out fi);
                Debug.Write(fi.achName + "\n");
            }
        }
        private bool createMediaType(Tag t, out AMMediaType am)
        {
            am = null;
            Fourcc fourcc = null;
            if (t.TagType == 9)
            {
                am = new AMMediaType();
                VideoTag vt = (VideoTag)t.data;
                if (vt.CodecID == 2)
                    fourcc = new Fourcc("1VLF");
                else if (vt.CodecID == 4 || vt.CodecID == 5)
                    fourcc = new Fourcc("4VLF");
                else
                    return false;
                am.formatPtr = IntPtr.Zero;
                am.unkPtr = IntPtr.Zero;
                //Tag t = T.Dequeue();
                am.majorType = MediaType.Video;
                am.subType = fourcc.ToGuid();
                am.formatType = FormatType.VideoInfo;
                BitmapInfoHeader bih = new BitmapInfoHeader();
                VideoInfoHeader vih = new VideoInfoHeader();
                vih.BmiHeader = bih;
                GetFrameSize(vt.data, vt.CodecID, out bih.Width, out bih.Height);
                Width = bih.Width;
                Height = bih.Height;
                bih.Compression = (int)fourcc.ToUInt32();
                //bih.Compression = Coding.ReadInt32(fourcc, 0);            
                am.formatSize = Marshal.SizeOf(typeof(VideoInfoHeader));
                am.formatPtr = Marshal.AllocCoTaskMem(am.formatSize);
                Marshal.StructureToPtr(vih, am.formatPtr, false);
            }
            else if (t.TagType == 8)
            {
                am = new AMMediaType();
                AudioTag at = (AudioTag)t.data;
                am.majorType = MediaType.Audio;
                am.formatType = FormatType.WaveEx;
                am.formatSize = Marshal.SizeOf(typeof(WaveFormatEx));
                am.formatPtr = Marshal.AllocCoTaskMem(am.formatSize);
                WaveFormatEx wfe = new WaveFormatEx();
                wfe.nSamplesPerSec = 44100 * (1 << at.SoundRate) / 8;
                wfe.wBitsPerSample = (short)(8 * (at.SoundRate + 1));
                wfe.nChannels = (short)(1 * (at.SoundType + 1));
                switch (at.SoundFormat)
                {
                    case 0:
                        wfe.wFormatTag = 0x0001; //WAVE_FORMAT_PCM Linear PCM, platform endian
                        fourcc = new Fourcc(0x0001);
                        am.subType = fourcc.ToGuid();
                        break;
                    case 1:
                        fourcc = new Fourcc("ASWF");   //ADPCM
                        am.subType = fourcc.ToGuid();
                        break;
                    case 2:
                        fourcc = new Fourcc(0x0055); //WAVE_FORMAT_MP3
                        am.subType = fourcc.ToGuid();
                        break;
                    case 4:
                        fourcc = new Fourcc("LLEN"); //Nellymoser 16 kHz mono
                        am.subType = fourcc.ToGuid();
                        wfe.nSamplesPerSec = 16000;
                        break;
                    case 5:
                        fourcc = new Fourcc("LLEN");   //Nellymoser 8 kHz mono
                        am.subType = fourcc.ToGuid();
                        wfe.nSamplesPerSec = 8000;  
                        break;
                    case 6:
                        fourcc = new Fourcc("LLEN");    //Nellymoser
                        am.subType = fourcc.ToGuid();
                        break;
                    case 10:
                        fourcc = new Fourcc(0x1610);  //AAC  WAVE_FORMAT_MPEG_HEAAC 
                        am.subType = fourcc.ToGuid();
                        break;
                    case 11: 
                        fourcc = new Fourcc("XEPS");  //SPEEX
                        am.subType = fourcc.ToGuid();
                        break;
                    case 3:   //Linear PCM, little endian
                    default:
                        am = null;
                        return false;
                }

                Marshal.StructureToPtr(wfe, am.formatPtr, false);
            }
            else return false;
            return true;
        }

        private void GetFrameSize(byte[] chunk, byte _codecID, out int _width, out int _height)
        {
            _width = 0;
            _height = 0;
            if (_codecID == 2)
            {
                // Reference: flv_h263_decode_picture_header from libavcodec's h263.c

                if (chunk.Length < 10) return;

                if ((chunk[1] != 0) || (chunk[2] != 0))
                {
                    return;
                }

                ulong x = BitConverterBE.ToUInt64(chunk, 3);
                int format;

                if (BitHelper.Read(ref x, 1) != 1)
                {
                    return;
                }
                BitHelper.Read(ref x, 5);
                BitHelper.Read(ref x, 8);

                format = BitHelper.Read(ref x, 3);
                switch (format)
                {
                    case 0:
                        _width = BitHelper.Read(ref x, 8);
                        _height = BitHelper.Read(ref x, 8);
                        break;
                    case 1:
                        _width = BitHelper.Read(ref x, 16);
                        _height = BitHelper.Read(ref x, 16);
                        break;
                    case 2:
                        _width = 352;
                        _height = 288;
                        break;
                    case 3:
                        _width = 176;
                        _height = 144;
                        break;
                    case 4:
                        _width = 128;
                        _height = 96;
                        break;
                    case 5:
                        _width = 320;
                        _height = 240;
                        break;
                    case 6:
                        _width = 160;
                        _height = 120;
                        break;
                    default:
                        return;
                }
            }
            else if ((_codecID == 4) || (_codecID == 5))
            {
                int skip = (_codecID == 4) ? 1 : 4;
                if (chunk.Length < (skip + 8)) return;
                ulong x = BitConverterBE.ToUInt64(chunk, skip);

                int deltaFrameFlag = BitHelper.Read(ref x, 1);
                int quant = BitHelper.Read(ref x, 6);
                int separatedCoeffFlag = BitHelper.Read(ref x, 1);
                int subVersion = BitHelper.Read(ref x, 5);
                int filterHeader = BitHelper.Read(ref x, 2);
                int interlacedFlag = BitHelper.Read(ref x, 1);

                if (deltaFrameFlag != 0)
                {
                    return;
                }
                if ((separatedCoeffFlag != 0) || (filterHeader == 0))
                {
                    BitHelper.Read(ref x, 16);
                }

                _height = BitHelper.Read(ref x, 8) * 16;
                _width = BitHelper.Read(ref x, 8) * 16;

            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
        }
    }

    internal class StreamHandler : IDisposable, INetStreamFilterCB
    {

        public Tag CurrTag;
        public IntPtr pp;
        public AMMediaType am;
        public IMemAllocator pAlloc = null;
        public StreamHandler()
        {
            pp = IntPtr.Zero;
        }
        public void Dispose()
        {
        }
        public int OnMediaTypeChanged(int hr, AMMediaType am)
        {
            this.am = am;
            return 0;
        }
        public int GetMediaType(int lPosition, AMMediaType am)
        {
            return 0;
        }
        public int CheckMediaType(AMMediaType am)
        {
            return 0;
        }
        public int SetTimeStamps(IMediaSample pSample)
        {
            return 0;
        }
        public int OnDecideBufferSize(IMemAllocator pAlloc, AllocatorProperties pRequest)
        {
            int hr = 0;
            pRequest.cBuffers = 100;
            pRequest.cbBuffer = (int)CurrTag.DataSize*4;
            AllocatorProperties actual = new AllocatorProperties();
            hr = pAlloc.SetProperties(pRequest, actual);
            if (actual.cBuffers !=100 || actual.cbBuffer != CurrTag.DataSize*4)
                return 1;
            this.pAlloc = pAlloc;
            //hr = pAlloc.Commit();
            return hr;
        }
        public int OnSetPinAllocator(IMemAllocator pAllocator)
        {
            int hr;
            AllocatorProperties props = new AllocatorProperties(), actual = new AllocatorProperties();
            hr = pAllocator.GetProperties(props);
            if (props.cbBuffer >= CurrTag.DataSize)
                return 0;
            //hr = pAllocator.Decommit();
            hr = props.cbBuffer = (int)CurrTag.DataSize * 10;
            hr = pAllocator.SetProperties(props, actual);
            Marshal.ThrowExceptionForHR(hr);
            //hr = pAllocator.Commit();
            return 0;
        }
        public int OnDeliverSample(IMediaSample pSample)
        {
            int hr;
            try
            {
                
                int nBytes = pSample.GetSize();
                byte[] data = null;
                if (CurrTag.TagType == 9)
                {
                    VideoTag vt = (VideoTag)CurrTag.data;
                    data = vt.data;
                }
                else if (CurrTag.TagType == 8)
                {
                    AudioTag at = (AudioTag)CurrTag.data;
                    data = at.data;
                }
                else
                    return 1;
                if (data.Length < 4)
                    return 1;
                if (nBytes < CurrTag.DataSize)
                    return 1;
                int size = Marshal.SizeOf(data[0]) * data.Length - 1;
                pp = IntPtr.Zero;
                pp = Marshal.AllocHGlobal(size);
                hr = pSample.GetPointer(out pp);
                Marshal.Copy(data, 1, pp, data.Length - 1);
                hr = pSample.SetActualDataLength(size);
                hr = pSample.SetTime(DsLong.FromInt64((long)CurrTag.StartTime), DsLong.FromInt64((long)CurrTag.EndTime));
                hr = pSample.SetMediaTime(null, null);
                //hr = pSample.SetMediaType(this.am);
                //hr = pSample.SetPreroll(false);
               // hr = pSample.SetSyncPoint(true);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in ondeliversample:" + ex.Message);
            }
            return 0;
        }
        public int Notify(IBaseFilter pSelf, Quality q)
        {
            return 0;
        }
    }

    [ComImport, Guid("3C9056DF-D77E-4eb3-BC48-C175C784763B")]
    public class NetStreamFilter
    {
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    Guid("37A11A90-52E1-4771-B360-83481308E605")]
    public interface INetStreamFilterCB
    {
        [PreserveSig]
        int CheckMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt);

        [PreserveSig]
        int GetMediaType(int lPosition, [In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt);

        [PreserveSig]
        int OnMediaTypeChanged(int hr, [In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt);

        [PreserveSig]
        int OnDecideBufferSize(IMemAllocator pAlloc, [In, MarshalAs(UnmanagedType.LPStruct)] AllocatorProperties request);

        [PreserveSig]
        int OnSetPinAllocator(IMemAllocator pAllocator);

        [PreserveSig]
        int OnDeliverSample(IMediaSample pSample);

        [PreserveSig]
        int Notify(IBaseFilter pSelf, [In, MarshalAs(UnmanagedType.Struct)] Quality q);


    }

    [ComImport, Guid("5985566C-4E04-4a58-91DE-F8E3F9B939AB"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface INetStreamFilterConfig
    {
        [PreserveSig]
        int SetPinMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt);

        [PreserveSig]
        int SetCallback(INetStreamFilterCB pfn);

        [PreserveSig]
        int DeliverPacket(UInt32 StartTime, UInt32 EndTime);

        [PreserveSig]
        int SetPinAllocator();
        [PreserveSig]
        int StartFlash();
        [PreserveSig]
        int FinishFlash();


    }
    public class StreamCompletedArgs : System.EventArgs
    {
        public EventCode Result;
        internal StreamCompletedArgs(EventCode ec)
        {
            Result = ec;
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct Packet
    {
        public byte[] pData;
        public long StartTime;
        public long EndTime;
        public long nBytes;
    }
    public struct Tag
    {
        public UInt32 PreviousTagSize;
        public UInt32 TagType;
        public UInt32 DataSize;
        public UInt64 StartTime;
        public UInt64 EndTime;
        public UInt32 Reserved;
        public object data;
    }
    public struct AudioTag
    {
        public Byte SoundFormat;
        public Byte SoundRate;
        public Byte SoundSize;
        public Byte SoundType;
        public byte[] data;
    }
    public struct VideoTag
    {
        public Byte FrameType;
        public Byte CodecID;
        public byte[] data;
    }
}

